﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;

namespace Infrastructure.Utils.Helpers
{
    /// <summary>
    /// 包含了与字符串加密解密相关的一些常用扩展方法
    /// </summary>
    /// <example>
    /// public string Password
    /// {
    /// 	get
    /// 	{
    /// 		return StringCipher.SimpleDecryptWithPassword(this.GetPassword().StringValue, this.AppKey, 0);
    ///      }
    ///      set
    /// 	{
    ///       this.GetPassword().StringValue = StringCipher.SimpleEncryptWithPassword(value, this.AppKey, null);
    ///      }
    ///  }
    /// </example>
    public static class EncryptHelper
    {
        #region AES

        private static readonly RandomNumberGenerator Random = RandomNumberGenerator.Create();
        public static readonly int BlockBitSize = 128;
        public static readonly int KeyBitSize = 256;
        public static readonly int SaltBitSize = 64;
        public static readonly int Iterations = 10000;
        public static readonly int MinPasswordLength = 12;
        public static byte[] NewKey()
        {
            byte[] array = new byte[EncryptHelper.KeyBitSize / 8];
            EncryptHelper.Random.GetBytes(array);
            return array;
        }

        /// <summary>
        /// 简单加密
        /// </summary>
        /// <param name="secretMessage"></param>
        /// <param name="cryptKey"></param>
        /// <param name="authKey"></param>
        /// <param name="nonSecretPayload"></param>
        /// <returns></returns>
        public static string SimpleEncrypt(string secretMessage, byte[] cryptKey, byte[] authKey, byte[] nonSecretPayload = null)
        {
            if (string.IsNullOrEmpty(secretMessage))
            {
                throw new ArgumentException("Secret Message Required!", "secretMessage");
            }
            byte[] bytes = Encoding.UTF8.GetBytes(secretMessage);
            byte[] inArray = EncryptHelper.SimpleEncrypt(bytes, cryptKey, authKey, nonSecretPayload);
            return Convert.ToBase64String(inArray);
        }

        /// <summary>
        /// 简单解密
        /// </summary>
        /// <param name="encryptedMessage"></param>
        /// <param name="cryptKey"></param>
        /// <param name="authKey"></param>
        /// <param name="nonSecretPayloadLength"></param>
        /// <returns></returns>
        public static string SimpleDecrypt(string encryptedMessage, byte[] cryptKey, byte[] authKey, int nonSecretPayloadLength = 0)
        {
            if (string.IsNullOrWhiteSpace(encryptedMessage))
            {
                throw new ArgumentException("Encrypted Message Required!", "encryptedMessage");
            }
            byte[] encryptedMessage2 = Convert.FromBase64String(encryptedMessage);
            byte[] bytes = EncryptHelper.SimpleDecrypt(encryptedMessage2, cryptKey, authKey, nonSecretPayloadLength);
            return Encoding.UTF8.GetString(bytes);
        }

        /// <summary>
        /// 使用密码对需保密消息进行简单加密
        /// </summary>
        /// <param name="secretMessage">需保密消息</param>
        /// <param name="password">密码</param>
        /// <param name="nonSecretPayload"></param>
        /// <returns></returns>
        public static string SimpleEncryptWithPassword(string secretMessage, string password, byte[] nonSecretPayload = null)
        {
            if (string.IsNullOrEmpty(secretMessage))
            {
                return "";
            }
            byte[] bytes = Encoding.UTF8.GetBytes(secretMessage);
            byte[] inArray = EncryptHelper.SimpleEncryptWithPassword(bytes, password, nonSecretPayload);
            return Convert.ToBase64String(inArray);
        }

        /// <summary>
        /// 使用密码对简单加密的信息进行解密
        /// </summary>
        /// <param name="encryptedMessage">加密后的信息</param>
        /// <param name="password">密码</param>
        /// <param name="nonSecretPayloadLength"></param>
        /// <returns></returns>
        public static string SimpleDecryptWithPassword(string encryptedMessage, string password, int nonSecretPayloadLength = 0)
        {
            if (string.IsNullOrWhiteSpace(encryptedMessage))
            {
                return "";
            }
            byte[] encryptedMessage2 = Convert.FromBase64String(encryptedMessage);
            byte[] array = EncryptHelper.SimpleDecryptWithPassword(encryptedMessage2, password, nonSecretPayloadLength);
            if (array != null)
            {
                return Encoding.UTF8.GetString(array);
            }
            return "";
        }

        private static byte[] SimpleEncrypt(byte[] secretMessage, byte[] cryptKey, byte[] authKey, byte[] nonSecretPayload = null)
        {
            if (cryptKey == null || cryptKey.Length != EncryptHelper.KeyBitSize / 8)
            {
                throw new ArgumentException(string.Format("Key needs to be {0} bit!", EncryptHelper.KeyBitSize), "cryptKey");
            }
            if (authKey == null || authKey.Length != EncryptHelper.KeyBitSize / 8)
            {
                throw new ArgumentException(string.Format("Key needs to be {0} bit!", EncryptHelper.KeyBitSize), "authKey");
            }
            if (secretMessage == null || secretMessage.Length < 1)
            {
                throw new ArgumentException("Secret Message Required!", "secretMessage");
            }
            nonSecretPayload = (nonSecretPayload ?? new byte[0]);
            byte[] iV;
            byte[] buffer;
            using (AesManaged aesManaged = new AesManaged
            {
                KeySize = EncryptHelper.KeyBitSize,
                BlockSize = EncryptHelper.BlockBitSize,
                Mode = CipherMode.CBC,
                Padding = PaddingMode.PKCS7
            })
            {
                aesManaged.GenerateIV();
                iV = aesManaged.IV;
                using (ICryptoTransform cryptoTransform = aesManaged.CreateEncryptor(cryptKey, iV))
                {
                    using (MemoryStream memoryStream = new MemoryStream())
                    {
                        using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Write))
                        {
                            using (BinaryWriter binaryWriter = new BinaryWriter(cryptoStream))
                            {
                                binaryWriter.Write(secretMessage);
                            }
                        }
                        buffer = memoryStream.ToArray();
                    }
                }
            }
            byte[] result;
            using (HMACSHA256 hMACSHA = new HMACSHA256(authKey))
            {
                using (MemoryStream memoryStream2 = new MemoryStream())
                {
                    using (BinaryWriter binaryWriter2 = new BinaryWriter(memoryStream2))
                    {
                        binaryWriter2.Write(nonSecretPayload);
                        binaryWriter2.Write(iV);
                        binaryWriter2.Write(buffer);
                        binaryWriter2.Flush();
                        byte[] buffer2 = hMACSHA.ComputeHash(memoryStream2.ToArray());
                        binaryWriter2.Write(buffer2);
                    }
                    result = memoryStream2.ToArray();
                }
            }
            return result;
        }
        private static byte[] SimpleDecrypt(byte[] encryptedMessage, byte[] cryptKey, byte[] authKey, int nonSecretPayloadLength = 0)
        {
            if (cryptKey == null || cryptKey.Length != EncryptHelper.KeyBitSize / 8)
            {
                throw new ArgumentException(string.Format("CryptKey needs to be {0} bit!", EncryptHelper.KeyBitSize), "cryptKey");
            }
            if (authKey == null || authKey.Length != EncryptHelper.KeyBitSize / 8)
            {
                throw new ArgumentException(string.Format("AuthKey needs to be {0} bit!", EncryptHelper.KeyBitSize), "authKey");
            }
            if (encryptedMessage == null || encryptedMessage.Length == 0)
            {
                throw new ArgumentException("Encrypted Message Required!", "encryptedMessage");
            }
            byte[] result;
            using (HMACSHA256 hMACSHA = new HMACSHA256(authKey))
            {
                byte[] array = new byte[hMACSHA.HashSize / 8];
                byte[] array2 = hMACSHA.ComputeHash(encryptedMessage, 0, encryptedMessage.Length - array.Length);
                int num = EncryptHelper.BlockBitSize / 8;
                if (encryptedMessage.Length < array.Length + nonSecretPayloadLength + num)
                {
                    result = null;
                }
                else
                {
                    Array.Copy(encryptedMessage, encryptedMessage.Length - array.Length, array, 0, array.Length);
                    int num2 = 0;
                    for (int i = 0; i < array.Length; i++)
                    {
                        num2 |= (int)(array[i] ^ array2[i]);
                    }
                    if (num2 != 0)
                    {
                        result = null;
                    }
                    else
                    {
                        using (AesManaged aesManaged = new AesManaged
                        {
                            KeySize = EncryptHelper.KeyBitSize,
                            BlockSize = EncryptHelper.BlockBitSize,
                            Mode = CipherMode.CBC,
                            Padding = PaddingMode.PKCS7
                        })
                        {
                            byte[] array3 = new byte[num];
                            Array.Copy(encryptedMessage, nonSecretPayloadLength, array3, 0, array3.Length);
                            using (ICryptoTransform cryptoTransform = aesManaged.CreateDecryptor(cryptKey, array3))
                            {
                                using (MemoryStream memoryStream = new MemoryStream())
                                {
                                    using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Write))
                                    {
                                        using (BinaryWriter binaryWriter = new BinaryWriter(cryptoStream))
                                        {
                                            binaryWriter.Write(encryptedMessage, nonSecretPayloadLength + array3.Length, encryptedMessage.Length - nonSecretPayloadLength - array3.Length - array.Length);
                                        }
                                    }
                                    result = memoryStream.ToArray();
                                }
                            }
                        }
                    }
                }
            }
            return result;
        }
        private static byte[] SimpleEncryptWithPassword(byte[] secretMessage, string password, byte[] nonSecretPayload = null)
        {
            nonSecretPayload = (nonSecretPayload ?? new byte[0]);
            if (string.IsNullOrWhiteSpace(password) || password.Length < EncryptHelper.MinPasswordLength)
            {
                throw new ArgumentException(string.Format("Must have a password of at least {0} characters!", EncryptHelper.MinPasswordLength), "password");
            }
            if (secretMessage == null || secretMessage.Length == 0)
            {
                throw new ArgumentException("Secret Message Required!", "secretMessage");
            }
            byte[] array = new byte[EncryptHelper.SaltBitSize / 8 * 2 + nonSecretPayload.Length];
            Array.Copy(nonSecretPayload, array, nonSecretPayload.Length);
            int num = nonSecretPayload.Length;
            byte[] bytes;
            using (Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, EncryptHelper.SaltBitSize / 8, EncryptHelper.Iterations))
            {
                byte[] salt = rfc2898DeriveBytes.Salt;
                bytes = rfc2898DeriveBytes.GetBytes(EncryptHelper.KeyBitSize / 8);
                Array.Copy(salt, 0, array, num, salt.Length);
                num += salt.Length;
            }
            byte[] bytes2;
            using (Rfc2898DeriveBytes rfc2898DeriveBytes2 = new Rfc2898DeriveBytes(password, EncryptHelper.SaltBitSize / 8, EncryptHelper.Iterations))
            {
                byte[] salt2 = rfc2898DeriveBytes2.Salt;
                bytes2 = rfc2898DeriveBytes2.GetBytes(EncryptHelper.KeyBitSize / 8);
                Array.Copy(salt2, 0, array, num, salt2.Length);
            }
            return EncryptHelper.SimpleEncrypt(secretMessage, bytes, bytes2, array);
        }
        private static byte[] SimpleDecryptWithPassword(byte[] encryptedMessage, string password, int nonSecretPayloadLength = 0)
        {
            if (string.IsNullOrWhiteSpace(password) || password.Length < EncryptHelper.MinPasswordLength)
            {
                throw new ArgumentException(string.Format("Must have a password of at least {0} characters!", EncryptHelper.MinPasswordLength), "password");
            }
            if (encryptedMessage == null || encryptedMessage.Length == 0)
            {
                throw new ArgumentException("Encrypted Message Required!", "encryptedMessage");
            }
            byte[] array = new byte[EncryptHelper.SaltBitSize / 8];
            byte[] array2 = new byte[EncryptHelper.SaltBitSize / 8];
            Array.Copy(encryptedMessage, nonSecretPayloadLength, array, 0, array.Length);
            Array.Copy(encryptedMessage, nonSecretPayloadLength + array.Length, array2, 0, array2.Length);
            byte[] bytes;
            using (Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, array, EncryptHelper.Iterations))
            {
                bytes = rfc2898DeriveBytes.GetBytes(EncryptHelper.KeyBitSize / 8);
            }
            byte[] bytes2;
            using (Rfc2898DeriveBytes rfc2898DeriveBytes2 = new Rfc2898DeriveBytes(password, array2, EncryptHelper.Iterations))
            {
                bytes2 = rfc2898DeriveBytes2.GetBytes(EncryptHelper.KeyBitSize / 8);
            }
            return EncryptHelper.SimpleDecrypt(encryptedMessage, bytes, bytes2, array.Length + array2.Length + nonSecretPayloadLength);
        }

        #endregion

        #region MD5
        /// <summary>
        /// 计算指定字符串的MD5值
        /// </summary>
        /// <param name="key">要计算Hash的字符串</param>
        /// <returns>字符串的Hash</returns>
        public static string MD5UTF8(string key)
        {
            return key.MD5(System.Text.Encoding.UTF8);
        }

        /// <summary>
        /// 计算指定字符串的MD5值
        /// </summary>
        /// <param name="key">要计算Hash的字符串</param>
        /// <param name="encoding">计算Hash的编码方法</param>
        /// <returns>字符串的Hash</returns>
        public static string MD5Key(string key, string encoding)
        {
            return key.MD5(System.Text.Encoding.GetEncoding(encoding));
        }

        /// <summary>
        /// 计算指定字符串的MD5值
        /// </summary>
        /// <param name="key">要计算Hash的字符串</param>
        /// <param name="encoding">计算Hash的编码方法</param>
        /// <returns>字符串的Hash</returns>
        public static string MD5(this string key, System.Text.Encoding encoding)
        {
            if (key == null) throw new ArgumentNullException();

            //获取加密服务  
            System.Security.Cryptography.MD5CryptoServiceProvider md5CSP = new System.Security.Cryptography.MD5CryptoServiceProvider();
            //获取要加密的字段，并转化为Byte[]数组  
            byte[] testEncrypt = encoding.GetBytes(key);
            //加密Byte[]数组  
            byte[] resultEncrypt = md5CSP.ComputeHash(testEncrypt);

            //将加密后的数组转化为字段(普通加密)  
            //string testResult = System.Text.Encoding.Unicode.GetString(resultEncrypt);  
            //作为密码方式加密   
            //string EncryptPWD = System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(test, "MD5");  

            return BitConverter.ToString(resultEncrypt).Replace("-", "").ToUpper();
        }
        #endregion

        /// <summary>
        /// 计算指定字节数组的MD5
        /// </summary>
        /// <param name="source">来源数组</param>
        /// <returns></returns>
        public static byte[] MD5(this byte[] source)
        {
            var md5 = System.Security.Cryptography.MD5CryptoServiceProvider.Create();
            return md5.ComputeHash(source);
        }

        /// <summary>
        /// 计算指定字节数组的MD5字符串
        /// </summary>
        /// <param name="source">来源数组</param>
        /// <returns></returns>
        public static string Md5String(this byte[] source)
        {
            return BitConverter.ToString(source.MD5()).Replace("-", "").ToUpper();
        }

        /// <summary>
        /// 计算指定字节数组的SHA1
        /// </summary>
        /// <param name="source">来源数组</param>
        /// <returns></returns>
        public static byte[] Sha1(this byte[] source)
        {
            var sha1 = System.Security.Cryptography.SHA1CryptoServiceProvider.Create();
            return sha1.ComputeHash(source);
        }

        /// <summary>
        /// 计算指定字节数组的SHA1
        /// </summary>
        /// <param name="source">来源数组</param>
        /// <returns></returns>
        public static string Sha1String(this byte[] source)
        {
            return BitConverter.ToString(source.Sha1()).Replace("-", "").ToUpper();
        }
    }
}
